package com.gitee.wsl.struct.either

import arrow.core.Either
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
import arrow.core.getOrElse
import com.gitee.wsl.ext.toResult


fun Boolean.toOptionUnit(): Option<Unit> =
    if (this) Some(Unit) else None

/**
 * Turns an [Option] value into a [Result].
 */

inline fun <A, B: Throwable> Option<A>.toResult(error: () -> B): Result<A> = this.getOrNull().toResult(error)


/**
 * Map some to Unit. This restores `.void()` which was deprecated by Arrow.
 */
fun <A> Option<A>.unit() = map { }

/**
 * Returns `this` if it's a Some, otherwise returns the `other` instance
 */
infix fun <T> Option<T>.or(other: () -> Option<T>): Option<T> = when (this) {
    is Some -> this
    is None -> other()
}


/**
 * Will return an empty string if the Optional value supplied is None
 */
fun <T> Option<T>.orEmpty(f: (T) -> String): String = this.map(f).getOrElse { "" }

/**
 * Map a function that returns an Either across the Option.
 *
 * If the option is a None, return a Right(None).
 * If the option is a Some, apply f to the value. If f returns a Right, wrap the result in a Some.
 */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
inline fun <T, E, U> Option<T>.traverse(f: (T) -> Either<E, U>): Either<E, Option<U>> =
    fold( { Either.Right(None) }, { a -> f(a).map { Some(it) } })

/**
 * Synonym for traverse((T)-> Either<E, U>): Either<E, Option<U>>
 */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
inline fun <T, E, U> Option<T>.traverseEither(f: (T) -> Either<E, U>): Either<E, Option<U>> =
    traverse(f)

/**
 * Map a function that returns an Iterable across the Option.
 *
 * If the option is a None, return an empty List
 * If the option is a Some, apply f to the value and wrap the result in a Some.
 */
inline fun <A, B> Option<A>.traverse(f: (A) -> Iterable<B>): List<Option<B>> =
    fold( { emptyList() }, { a -> f(a).map { Some(it) } })